/*
 * systrace - Should be a tool to analyze system call traces but can be used
 *            to analyze sequences of generic strings
 *
 * Copyright (c) 2011 Matteo Cicuttin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

#include <vector>

#include "StreamProcessor.h"
#include "BlockAllFilter.h"

using namespace std;

StreamProcessor::StreamProcessor()
{
	_basestreamer = new Streamer();
	_streamer = _basestreamer;
}

bool
StreamProcessor::open(const string& filename)
{
	bool retval;
	retval = _basestreamer->open(filename);
	_tracefilename = filename;
	return retval;
}

int
StreamProcessor::run(void)
{	
	if (!_streamer->has_data())
    {
        cout << "Cannot get data from " << _tracefilename << endl;
        return 1;
    }
	
	play();
	print();
	
	return 0;
}

void
StreamProcessor::play(void)
{
	int processed_samples;
	string sample;
	list<Analysis *>::iterator itor;
	processed_samples = 0;
	while ( _streamer->has_data() )
	{
		sample = _streamer->get_sample();
		
		for(itor = _analysislist.begin(); itor != _analysislist.end(); itor++)
			if ( (*itor)->enabled() )
				(*itor)->add_sample(sample);
		
		processed_samples++;
		
		if (processed_samples % 10000 == 0)
		{
			cout << "Processed " << processed_samples << " syscalls" << "\r";
			cout.flush();
		}
		
	}
	cout << "Processed " << processed_samples << " syscalls" << endl;
	
}

void
StreamProcessor::print(void)
{
	list<Analysis *>::iterator itor;
	for(itor = _analysislist.begin(); itor != _analysislist.end(); itor++)
		if ( (*itor)->enabled() )
			(*itor)->print_result(cout);
}

void
StreamProcessor::enable(const string& m)
{
	if ( _modmap.find(m) == _modmap.end() )
	{
		cout << "No such module " << m << endl;
		return;
	}
	
	_modmap[m]->set_enabled(true);
}

void
StreamProcessor::disable(const string& m)
{
	if ( _modmap.find(m) == _modmap.end() )
	{
		cout << "No such module " << m << endl;
		return;
	}
	
	_modmap[m]->set_enabled(false);
}

void
StreamProcessor::listmods(void)
{
	list<Analysis *>::iterator aitor;
	for(aitor = _analysislist.begin(); aitor != _analysislist.end(); aitor++)
	{
		if ( (*aitor)->enabled() )
			cout << "Analysis \x1B[1;33m" << (*aitor)->module_name() << "\x1B[0m \t\t enabled" << endl;
		else
			cout << "Analysis \x1B[1;33m" << (*aitor)->module_name() << "\x1B[0m \t\t disabled" << endl;
	}
	
	list<Filter *>::iterator fitor;
	for(fitor = _filterlist.begin(); fitor != _filterlist.end(); fitor++)
	{
		if ( (*fitor)->enabled() )
			cout << "Filter \x1B[1;33m" << (*fitor)->module_name() << "\x1B[0m \t\t enabled" << endl;
		else
			cout << "Filter \x1B[1;33m" << (*fitor)->module_name() << "\x1B[0m \t\t disabled" << endl;
	}
}

void
StreamProcessor::flush(void)
{
	list<Analysis *>::iterator itor;
	for(itor = _analysislist.begin(); itor != _analysislist.end(); itor++)
		(*itor)->flush();
}

void
StreamProcessor::insertfilter(const string& filtername)
{
	if (filtername == "blockall")
	{
		BlockAllFilter *bf = new BlockAllFilter(_streamer);
		add_module(bf);
		_streamer = bf;
	}
}

void
StreamProcessor::rewind(void)
{
	_basestreamer->rewind();
}

void
StreamProcessor::help(void)
{
	cout << "  \x1B[1mopen <filename>\x1B[0m      open the specified trace file" << endl;
	cout << "  \x1B[1mclose\x1B[0m                close currently open file" << endl;
	cout << "  \x1B[1mplay\x1B[0m                 process the file using enabled analysis modules" << endl;
	cout << "  \x1B[1mprint\x1B[0m                print results of all enabled analysis modules" << endl;
	cout << "  \x1B[1menable <module>\x1B[0m      enable specified module" << endl;
	cout << "  \x1B[1mdisable <module>\x1B[0m     disable specified module" << endl;
	cout << "  \x1B[1mlistmods\x1B[0m             list modules and status" << endl;
	cout << "  \x1B[1mexit\x1B[0m                 quit systrace" << endl;
	cout << "  \x1B[1m<module name>\x1B[0m        switch to module context" << endl;
	cout << "  \x1B[1mflush\x1B[0m                flush data structures of all modules" << endl;
	cout << "  \x1B[1mrewind\x1B[0m               rewind the input stream" << endl;
}

int
StreamProcessor::run_interactive(void)
{
	vector<string> tokens;
	char *s, *pch;
	
	cout << "Systrace command interpreter. Type \x1B[1mhelp\x1B[0m for a list of commands" << endl;
	
	while (1)
	{
		//cout << "\x1B[1msystrace >>\x1B[0m ";
		//getline(cin, line);
		
		//s = strdup(line.c_str());
		
		s = readline("\x1B[1msystrace >>\x1B[0m ");
		add_history(s);
		
		pch = strtok(s, " ");
		while (pch)
		{
			tokens.push_back(pch);
			pch = strtok(NULL, " ");
		}
		
		if ( tokens.size() == 0 )
			continue;
		
		if ( tokens.at(0) == "open" )
		{
			if (tokens.size() == 2)
			{
				if ( !open(tokens.at(1)) )
					cout << "Cannot open " << tokens.at(1) << endl;
			}
			else
				cout << "Specify the file!" << endl;
		}
		
		if ( tokens.at(0) == "close" )
		{
			flush();
			_basestreamer->close();
		}
		
		if ( tokens.at(0) == "play" )
		{
			if (!_basestreamer->is_open())
			{
				cout << "Open a file first!" << endl;
				tokens.clear();
				free(s);
				continue;
			}
			_basestreamer->rewind();
			play();
		}
		
		if ( tokens.at(0) == "print" )
			print();
		
		if ( tokens.at(0) == "exit" )
			break;
		
		if ( tokens.at(0) == "help" )
			help();
		
		if ( tokens.at(0) == "enable" )
			if (tokens.size() == 2)
				enable( tokens.at(1) );
			else
				cout << "Specify module!" << endl;
		
		if ( tokens.at(0) == "disable" )
			if (tokens.size() == 2)
				disable( tokens.at(1) );
			else
				cout << "Specify module!" << endl;
		
		if ( tokens.at(0) == "insertfilter" )
			if (tokens.size() == 2)
				insertfilter( tokens.at(1) );
			else
				cout << "Specify filter!" << endl;
		
		if ( tokens.at(0) == "listmods" )
			listmods();
		
		if ( tokens.at(0) == "flush" )
			flush();
		
		if ( tokens.at(0) == "rewind" )
			rewind();
		
		if ( _modmap.find( tokens.at(0)) != _modmap.end() )
			_modmap[tokens.at(0)]->run_interactive();
		
		tokens.clear();
		free(s);
	}
	
	_basestreamer->close();
	return 0;
}

void
StreamProcessor::add_module(Analysis *a)
{
	_analysislist.push_back(a);
	_modmap[a->module_name()] = a;
	cout << "Analysis \x1B[1;33m" << a->module_name() << "\x1B[0m loaded" << endl;
}

void
StreamProcessor::add_module(Filter *f)
{
	_filterlist.push_back(f);
	_modmap[f->module_name()] = f;
	cout << "Filter \x1B[1;33m" << f->module_name() << "\x1B[0m loaded" << endl;
}

